home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 3 / Info_Mac_1994-01.iso / Development / Information / Mac Programming Secrets 1.0.1 / Chapter 04 / Progress Indicator.c < prev    next >
C/C++ Source or Header  |  1992-05-19  |  8KB  |  254 lines

  1. #include "Progress Indicator.h"
  2. #include "Standard Stuff.h"            // for "gMac"
  3.  
  4. /*******************************************************************************
  5.  
  6.     Progress indicator routines
  7.  
  8. *******************************************************************************/
  9.  
  10. /*******************************************************************************
  11.  
  12.     Private routines for the progress indicator routines. You’ll never have to
  13.     set or look at these values. The first three variables are initialized by
  14.     calling InitProgressIndicator. The remaining variables are used
  15.     internally.
  16.  
  17. *******************************************************************************/
  18.  
  19. GrafPtr    pProgressPort;    /*    Port to be used for drawing. When updating the
  20.                             indicator, we temporarily switch to this port,
  21.                             restoring the old port when we are done. */
  22.  
  23. Rect    pRect;            /*    Rectangle used for the progress indicator. The
  24.                             entire indicator is drawn within this rectangle.
  25.                             pRect is used to frame the indicator, and is then
  26.                             inset by a pixel to draw the insides. */
  27.  
  28. long    pMax;            /*    Maximum value for the process. This maximum can be
  29.                             any value. The indicator is drawn such that the
  30.                             ratio of the completed part to the entire part is
  31.                             the same as the current value to the max value. */
  32.  
  33. long    pCurrent;        /*    Current value. Set to zero when you call
  34.                             InitProgressIndicator. It is updated by calling
  35.                             SetProgress or SetProgressDelta. */
  36.  
  37. long    pLastCurrent;    /*    The last value of pCurrent that was used to draw
  38.                             the indicator. This is used to check if we need to
  39.                             do any drawing; if pLastCurrent == pCurrent, we
  40.                             don’t need to draw anything. */
  41.  
  42. short    pLastBorder;    /*    The location of the division between the “completed”
  43.                             part and the “to be completed” part of our indicator
  44.                             the last time we drew it. We use this internally
  45.                             (along with pLastCurrent) for determining if the
  46.                             indicator needs to be updated. */
  47.  
  48.  
  49. /*******************************************************************************
  50.  
  51.     InitProgressIndicator
  52.  
  53.     Call this just before you start your periodic task. All you have to do is
  54.     tell the progress indicator routines what port you want to draw in, the
  55.     rectangle you want to use, and the maximum value for your indicator.
  56.     Later, you’ll make repeated calls to update your progress value (which can
  57.     be anything from zero to the maximum value), which will update the
  58.     “thermometer” in the window.
  59.  
  60. *******************************************************************************/
  61. void InitProgressIndicator(GrafPtr ourPort, Rect r, long max)
  62. {
  63.     pProgressPort = ourPort;
  64.     pRect = r;
  65.     pMax = max;
  66.     pCurrent = 0;
  67.     pLastCurrent = 0;
  68.     pLastBorder = 0;
  69. }
  70.  
  71.  
  72. /*******************************************************************************
  73.  
  74.     SetProgress
  75.  
  76.     Call this periodically to update the indicator. Pass in the new value that
  77.     the indicator is supposed to represent. The value passed in will be
  78.     clipped between 0 and the maximum value.
  79.  
  80. *******************************************************************************/
  81. void SetProgress(long absoluteAmount)
  82. {
  83.     pCurrent = absoluteAmount;
  84.     DrawProgressBar();
  85. }
  86.  
  87.  
  88. /*******************************************************************************
  89.  
  90.     SetProgressDelta
  91.  
  92.     Call this periodically to update the indicator. Pass in a delta value to
  93.     be added to the current value. The result will be clipped between 0 and
  94.     the maximum value. The indicator will them be updated to indicate the new
  95.     current value.
  96.  
  97.     This routine returns TRUE if we hit or passed the maximum value. This
  98.     could be handy to determine if we are done doing whatever periodic task we
  99.     are representing.
  100.  
  101. *******************************************************************************/
  102. Boolean SetProgressDelta(long delta)
  103. {
  104.     pCurrent += delta;
  105.     DrawProgressBar();
  106.     return (pCurrent >= pMax);
  107. }
  108.  
  109.  
  110. /*******************************************************************************
  111.  
  112.     DrawProgressBar
  113.  
  114.     Internal routine to update internal variables after the client has called
  115.     SetProgress() or SetProgressDelta(). It calculates the width of the new
  116.     rectangle; if it’s changed since the last time, we call our update routine.
  117.  
  118. *******************************************************************************/
  119. void DrawProgressBar()
  120. {
  121.     short        border;
  122.     short        rectWidth;
  123.  
  124.     if ((pLastCurrent != pCurrent) && (pCurrent <= pMax)) {
  125.         pLastCurrent = pCurrent;
  126.         rectWidth = pRect.right - pRect.left - 2;
  127.         border = pRect.left + 1 + rectWidth * pCurrent / pMax;
  128.         if (pLastBorder != border) {
  129.             pLastBorder = border;
  130.             UpdateProgressBar();
  131.         }
  132.     }
  133. }
  134.  
  135.  
  136. /*******************************************************************************
  137.  
  138.     UpdateProgressBar
  139.  
  140.     Performs the actual drawing of the indicator. This routine is called
  141.     internally from other progress indicator routine to draw the new state of
  142.     the indicator. However, it can also be called in response to update events
  143.     by outside clients.
  144.  
  145. *******************************************************************************/
  146. void UpdateProgressBar()
  147. {
  148.     GrafPtr        oldPort;
  149.     RGBColor    oldForeColor;
  150.     RGBColor    oldBackColor;
  151.  
  152.     Rect        doneRect;
  153.     Rect        toDoRect;
  154.  
  155.     doneRect = pRect;
  156.     InsetRect(&doneRect, 1, 1);
  157.     toDoRect = doneRect;
  158.  
  159.     doneRect.right = toDoRect.left = pLastBorder;
  160.  
  161.     GetPort(&oldPort);
  162.     SetPort(pProgressPort);
  163.     GetForeColor(&oldForeColor);
  164.     GetBackColor(&oldBackColor);
  165.     PenNormal();
  166.  
  167.     SetFrameColor();
  168.     FrameRect(&pRect);
  169.  
  170.     SetDoneColor();
  171.     PaintRect(&doneRect);
  172.  
  173.     SetToDoColor();
  174.     PaintRect(&toDoRect);
  175.  
  176.     RGBForeColor(&oldForeColor);
  177.     RGBBackColor(&oldBackColor);
  178.     SetPort(oldPort);
  179. }
  180.  
  181.  
  182. /*******************************************************************************
  183.  
  184.     Progress indicator color routines.
  185.  
  186.     These are called to set the colors for the three different parts of the
  187.     progress indicator: the frame, the “Done” part (the stuff on the left side
  188.     of the indicator), and the “To Do” part (the stuff on the right).
  189.  
  190.     Note that we only check to see if we have Color QuickDraw or not; we don’t
  191.     also make a check to see how deep our monitor is. Instead, we simply let
  192.     QuickDraw match the color we specify to one most closely matching it on
  193.     whatever screen we’re drawing.
  194.  
  195.     However, if we only set the foreground color, we run headfirst into
  196.     QuickDraw as it tries to help us out behind our backs. Take the case of
  197.     setting the “To Do” color. For that color, we use red = 0xCCCC, green =
  198.     0xCCCC, and blue = 0xFFFF. This gives us a nice steel blue color in 8-bit
  199.     mode, and a nice light gray in 4- and 2- bit modes. However, it draws as
  200.     black in 1-bit mode, when we expect and need it to draw in white.
  201.  
  202.     What’s happening is this: when QuickDraw takes the color you specify and
  203.     tries to match it to one of the colors in the screen device’s color table,
  204.     it makes a special check to see if it maps your color into the background
  205.     color. If so, it inverts the color and tries again. In our case, our steel
  206.     blue gets mapped into white, which is the background color. QuickDraw says
  207.     “Oops! I’m about to draw something that won’t show up, so I’ll use a
  208.     different color, instead!” So it changes {0xCCCC, 0xCCCC, 0xFFFF} into
  209.     {0x3333, 0x3333, 0x0000} and remaps it. This time, the color maps into
  210.     black, which is what QuickDraw ends up using.
  211.  
  212.     To avoid this feature, we take control of the background color. When we
  213.     want our light colors to map into white, we change the background color to
  214.     black, avoiding any conflict. Alternatively, we could attach a palette to
  215.     our window containing the two colors we want as “courteous” entries. This
  216.     is sufficient to get QuickDraw to stop being so helpful.
  217.  
  218. *******************************************************************************/
  219.  
  220. const RGBColor    kBlack        = {0x0000, 0x0000, 0x0000};
  221. const RGBColor    kWhite        = {0xFFFF, 0xFFFF, 0xFFFF};
  222. const RGBColor    kDarkGrey    = {0x4000, 0x4000, 0x4000};
  223. const RGBColor    kSteelBlue    = {0xCCCC, 0xCCCC, 0xFFFF};
  224.  
  225. void SetFrameColor()
  226. {
  227.     if (gMac.hasColorQD) {
  228.         RGBForeColor(&kBlack);
  229.         RGBBackColor(&kWhite);
  230.     } else {
  231.         PenPat(qd.black);
  232.     }
  233. }
  234.  
  235. void SetDoneColor()
  236. {
  237.     if (gMac.hasColorQD) {
  238.         RGBForeColor(&kDarkGrey);
  239.         RGBBackColor(&kWhite);
  240.     } else {
  241.         PenPat(qd.black);
  242.     }
  243. }
  244.  
  245. void SetToDoColor()
  246. {
  247.     if (gMac.hasColorQD) {
  248.         RGBForeColor(&kSteelBlue);
  249.         RGBBackColor(&kBlack);
  250.     } else {
  251.         PenPat(qd.white);
  252.     }
  253. }
  254.